package com.example.palette.crash;

import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Debug;
import android.os.Environment;
import android.os.Process;
import android.os.StatFs;
import android.provider.Settings;
import android.text.format.Formatter;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;

import java.io.File;
import java.math.BigDecimal;
import java.net.NetworkInterface;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class JavaCrashHandler implements Thread.UncaughtExceptionHandler {

    private static final String TAG = "JavaCrashHandler";
    private static JavaCrashHandler INSTANCE;
    private SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
    private Context mContext;
    private Date date = new Date();
    private StringBuilder builder = new StringBuilder();
    private JavaCrashHandler() {
    }

    public static JavaCrashHandler getInstance() {
        if (INSTANCE == null) {
            synchronized (JavaCrashHandler.class) {
                if (INSTANCE == null) {
                    INSTANCE = new JavaCrashHandler();
                }
            }
        }
        return INSTANCE;
    }

    public void init(Context context) {
        mContext = context;
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        Log.e(TAG, collectInfos(t, e));
        Process.killProcess(Process.myPid());
        System.exit(0);
    }

    @SuppressLint("HardwareIds")
    private String collectInfos(Thread t, Throwable e) {
        builder.setLength(0);
        date.setTime(System.currentTimeMillis());
        builder.append("\n基本信息->崩溃时间:").append(dataFormat.format(date));
        builder.append("\n基本信息->崩溃线程:").append(t.getName());
        builder.append("\n基本信息->崩溃堆栈:");
        if (e != null) {
            StackTraceElement[] stackTrace = e.getStackTrace();
            Throwable[] throwables = e.getSuppressed();
            Throwable cause = e.getCause();
            builder.append("\n" + e.toString());
            for (StackTraceElement traceElement : stackTrace) {
                builder.append("\nat " + traceElement);
            }
            for (Throwable se : throwables) {
                builder.append("\nat " + se.toString());
            }
            if (cause != null) {
                builder.append("\nat " + cause.toString());
            }
        }
        builder.append("\n应用信息->名称:").append(getAppName(mContext));
        builder.append("\n应用信息->id:").append(getPackageName(mContext));
        builder.append("\n应用信息->数字版本号:").append(getVersionCode(mContext));
        builder.append("\n应用信息->显示版本号:").append(getVersionName(mContext));
        builder.append("\n内存信息->系统分配应用最大内存:").append(getMaxMemory(mContext)).append(" MB");
        builder.append("\n内存信息->设置largeHeap,系统分配应用最大内存:").append(getLargeMaxMemory(mContext)).append(" MB");
        builder.append("\n内存信息->应用当前运行内存:").append(getRunningMemory(mContext));
        builder.append("\n设备信息->屏幕尺寸像素:宽->").append(getWidthPx(mContext)).append(" ,高->").append(getHeightPx(mContext));
        builder.append("\n设备信息->安卓系统版本:").append(Build.VERSION.RELEASE);
        builder.append("\n设备信息->安卓SDK版本:").append(Build.VERSION.SDK_INT);
        builder.append("\n设备信息->AndroidId:").append(Settings.Secure.getString(mContext.getApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID));
        builder.append("\n设备信息->CPU架构:").append(Build.CPU_ABI);
        builder.append("\n设备信息->厂商:").append(Build.MANUFACTURER);
        builder.append("\n设备信息->品牌:").append(Build.BRAND);
        builder.append("\n设备信息->id:").append(Build.ID);
        builder.append("\n设备信息->型号:").append(Build.MODEL == null ? "" : Build.MODEL.trim().replaceAll("\\s*", ""));
        builder.append("\n设备信息->SD卡总大小:").append(getSDTotalSize(mContext));
        builder.append("\n设备信息->SD卡可用大小:").append(getSDAvailableSize(mContext));
        builder.append("\n设备信息->MAC地址:").append(getMacAddress(mContext));
        return builder.toString();
    }

    private int getLargeMaxMemory(Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        return am.getLargeMemoryClass();
    }

    private int getMaxMemory(Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        return am.getMemoryClass();
    }

    private String getRunningMemory(Context context) {
        double memory = 0.0d;
        try {
            ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            Debug.MemoryInfo[] memoryInfo = activityManager.getProcessMemoryInfo(new int[]{Process.myPid()});
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (memoryInfo.length > 0) {
                    Debug.MemoryInfo info = memoryInfo[0];
                    String java_heap = info.getMemoryStat("summary.java-heap");
                    String native_heap = info.getMemoryStat("summary.native-heap");
                    String graphics = info.getMemoryStat("summary.graphics");
                    String stack = info.getMemoryStat("summary.stack");
                    String code = info.getMemoryStat("summary.code");
                    String system = info.getMemoryStat("summary.system");
                    String private_other = info.getMemoryStat("summary.private-other");
                    int kb = Integer.parseInt(java_heap) + Integer.parseInt(native_heap) + Integer.parseInt(graphics)
                            + Integer.parseInt(stack) + Integer.parseInt(code) + Integer.parseInt(system) + Integer.parseInt(private_other);
                    memory = kb / 1024d;
                }
            } else {
                if(memoryInfo.length > 0){
                    Debug.MemoryInfo info = memoryInfo[0];
                    int java_heap = info.dalvikPrivateDirty;
                    int native_heap = info.nativePrivateDirty;
                    int private_other = info.otherPrivateDirty;
                    int system;
                    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
                        system = info.getTotalPss() - info.getTotalPrivateDirty() - info.getTotalPrivateClean();
                    } else {
                        system = info.getTotalPss() - info.getTotalPrivateDirty();
                    }
                    int kb = java_heap + native_heap + private_other + system;
                    memory = kb / 1024d;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        BigDecimal result2 = new BigDecimal(Double.toString(memory));
        return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + " MB";
    }

    /**
     * 获取SD卡总大小
     *
     * @param context
     * @return
     */
    private String getSDTotalSize(Context context) {
        File path = Environment.getExternalStorageDirectory();
        StatFs stat = new StatFs(path.getPath());
        long blockSize = stat.getBlockSize();
        long totalBlocks = stat.getBlockCount();
        return Formatter.formatFileSize(context, blockSize * totalBlocks);
    }

    /**
     * 获取SD卡剩余大小
     *
     * @param context
     * @return
     */
    private String getSDAvailableSize(Context context) {
        File path = Environment.getExternalStorageDirectory();
        StatFs stat = new StatFs(path.getPath());
        long blockSize = stat.getBlockSize();
        long availableBlocks = stat.getAvailableBlocks();
        return Formatter.formatFileSize(context, blockSize * availableBlocks);
    }

    /**
     * 获取mac地址
     * @param context
     * @return
     */
    private String getMacAddress(Context context) {
        String macAddress = getMacAddressByWifiInfo(context);
        if (!"02:00:00:00:00:00".equals(macAddress)) {
            return macAddress;
        }
        macAddress = getMacAddressByNetworkInterface();
        if (!"02:00:00:00:00:00".equals(macAddress)) {
            return macAddress;
        }
        return "02:00:00:00:00:00";
    }

    /**
     * <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     *
     * @param context
     * @return
     */
    @SuppressLint({"HardwareIds", "MissingPermission"})
    private String getMacAddressByWifiInfo(Context context) {
        String result = "02:00:00:00:00:00";
        try {
            if (isWifi(context)) {
                @SuppressLint("WifiManagerLeak")
                WifiManager wifi = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
                if (wifi != null) {
                    WifiInfo info = wifi.getConnectionInfo();
                    if (info != null) {
                        return info.getMacAddress();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    @SuppressLint("MissingPermission")
    private boolean isWifi(Context context) {
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        return networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
    }

    /**
     * <uses-permission android:name="android.permission.INTERNET" />
     *
     * @return
     */
    private static String getMacAddressByNetworkInterface() {
        String result = "02:00:00:00:00:00";
        try {
            List<NetworkInterface> nis = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface ni : nis) {
                if (!ni.getName().equalsIgnoreCase("wlan0")) continue;
                byte[] macBytes = ni.getHardwareAddress();
                if (macBytes != null && macBytes.length > 0) {
                    StringBuilder res1 = new StringBuilder();
                    for (byte b : macBytes) {
                        res1.append(String.format("%02x:", b));
                    }
                    return res1.deleteCharAt(res1.length() - 1).toString();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    private int getVersionCode(Context context) {
        try {
            PackageManager packageManager = context.getPackageManager();
            PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
            return packageInfo.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return -1;
    }

    private String getVersionName(Context context) {
        try {
            PackageManager packageManager = context.getPackageManager();
            PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
            return packageInfo.versionName;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return "unknown";
    }

    private String getPackageName(Context context) {
        return context.getPackageName();
    }

    private String getAppName(Context context) {
        try {
            PackageManager packageManager = context.getPackageManager();
            return packageManager.getApplicationLabel(context.getApplicationInfo()).toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "unknown";
    }

    /**
     * 获取屏幕宽度
     *
     * @param context
     * @return
     */
    private int getWidthPx(Context context) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.widthPixels;
    }

    /**
     * 获取屏幕高度
     *
     * @param context
     * @return
     */
    private int getHeightPx(Context context) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.heightPixels;
    }
}
